//============================================================================================
/**
 * @file	vm.c
 * @brief	仮想インタープリタマシン　メイン部分(スクリプトコマンド実行に使用される)
 * @author	Sousuke Tamada
 * @date	01.11.07
 *
 * 03.04.15	Satoshi Nohara
 * 05.04.25 Hiroyuki Nakamura
 *
 * エメラルドのファイルがベース ( R/S -> FR/LG -> EME -> D/P )
 */
//============================================================================================
#include "common.h"

#define __VM_H_GLOBAL
#include "vm.h"

///スクリプトデバッグコードに必要なヘッダのinclude	by iwasawa 07.11.05
#ifdef PM_DEBUG
#include "fieldsys.h"
#include "field_event.h"
#endif	//PM_DEBUG

//============================================================================================
//	定義
//============================================================================================
// 仮想マシンの動作状態定義
enum{
	VMSTAT_READY,		// 停止(動作終了）
	VMSTAT_RUN,			// 動作中
	VMSTAT_WAIT,		// 待ち状態（チェックルーチン呼び出し）
};


//============================================================================================
//	グローバル変数
//============================================================================================
//const u32 ScriptBreakPoint = 0;


//============================================================================================
//
//
// 仮想マシン用関数
//
//
//============================================================================================

//--------------------------------------------------------------------------------------------
/**
 * 仮想マシン初期化
 *
 * @param	core		仮想マシン制御構造体へのポインタ
 * @param	cmd_tbl		命令テーブル開始アドレス
 * @param	cmd_max		命令テーブルの大きさ
 *
 * @return	none
 */
//--------------------------------------------------------------------------------------------
void VM_Init( VM_MACHINE * core, const VM_CMD * cmd_tbl, u32 cmd_max)
{
	u32	i;

	core->status = VMSTAT_READY;
	core->PC = NULL;
	core->SP = 0;
	core->id = 0;
	core->routine = NULL;
	core->command_table = cmd_tbl;
	core->cmd_max = cmd_max;
	for( i=0; i<VM_REG_MAX; i++ ){
		core->reg[i] = 0;
	}
	for( i=0; i<VM_STACK_MAX; i++ ){
		core->array[i] = NULL;
	}

#ifdef OLD_MSG_SYS
	core->pMsg = NULL;
#endif

#if 0
	//script.c EV_SCRIPT_WORKへ移動
	for( i=0; i<VM_WORK_MAX; i++ ){
		core->scr_work[i] = 0;
	}
#endif

	core->event_work = NULL;		//(06.07.20)
}


//--------------------------------------------------------------------------------------------
/**
 * 仮想マシンにコードを設定
 *
 * @param	core		仮想マシン制御構造体へのポインタ
 * @param	start		実行コードの開始アドレス
 *
 * @return	常にTRUE
 */
//--------------------------------------------------------------------------------------------
u8 VM_Start( VM_MACHINE * core, const VM_CODE * start )
{
	core->PC = start;
	core->status = VMSTAT_RUN;

	return TRUE;
}

//--------------------------------------------------------------------------------------------
/**
 * 仮想マシンをウェイト状態に設定
 *
 * @param	core		仮想マシン制御構造体へのポインタ
 * @param	func		ウェイト関数
 *
 * @return	none
 *
 * @li	TRUEが返ってくるまでウェイト関数を毎回呼びだす
 */
//--------------------------------------------------------------------------------------------
void VM_SetWait( VM_MACHINE * core, VM_WAIT_FUNC func )
{
	core->status = VMSTAT_WAIT;
	core->routine = func;
}

//--------------------------------------------------------------------------------------------
/**
 * 仮想マシン実行終了
 *
 * @param	core		仮想マシン制御構造体へのポインタ
 *
 * @return	none
 */
//--------------------------------------------------------------------------------------------
void VM_End( VM_MACHINE * core )
{
	core->status = VMSTAT_READY;
	core->PC = NULL;
}

//--------------------------------------------------------------------------------------------
/**
 * コマンドなどで参照するワークをセット
 *
 * @param	core		仮想マシン制御構造体へのポインタ
 * @param	work		ワークのポインタ
 *
 * @return	none
 */
//--------------------------------------------------------------------------------------------
void VM_SetWork( VM_MACHINE * core, void * work )
{
	core->event_work = work;
}

//--------------------------------------------------------------------------------------------
/**
 * 仮想マシン制御メイン
 *
 * @param	core		仮想マシン制御構造体へのポインタ
 *
 * @retval	"TRUE = 実行中"
 * @retval	"FALSE = 停止中・実行終了"
 */
//--------------------------------------------------------------------------------------------
u8 VM_Control( VM_MACHINE * core )
{
	u16 code;

	if( core->status == VMSTAT_READY ){ return FALSE; }

	switch( core->status ){
	case VMSTAT_READY:
		return FALSE;

	case VMSTAT_WAIT:
		if( core->routine != NULL ){
			if( core->routine(core) == TRUE ){
				core->status = VMSTAT_RUN;
			}
			return TRUE;
		}
		core->status = VMSTAT_RUN;
		/* FALL THROUGH */

	case VMSTAT_RUN:
		while( TRUE ){
			if( core->PC == NULL ){
				core->status = VMSTAT_READY;
				return FALSE;
			}
/*	デバッグ処理
			if( core->PC == (VM_CODE *)ScriptBreakPoint ){
				while( TRUE ){
					Halt();
				}
			}
*/

			code = VMGetU16( core );

			if( code >= core->cmd_max ) {
				GF_ASSERT_MSG(0, "command error %04x:%08x\n",code, core->PC - 2);
				core->status = VMSTAT_READY;
				return FALSE;
			}
			if( core->command_table[code]( core ) == 1 ){
				break;
			}
		}
	}
	return TRUE;
}


//============================================================================================
// 仮想マシン制御用サブルーチン
//============================================================================================

//--------------------------------------------------------------------------------------------
/**
 * 仮想マシンスタックプッシュ
 *
 * @param	core		仮想マシン制御構造体へのポインタ
 * @param	val
 *
 * @retval	"0 = 正常"
 * @retval	"1 = エラー"
 */
//--------------------------------------------------------------------------------------------
u8 VMStackPush( VM_MACHINE * core, const VM_CODE * val )
{
	if( core->SP + 1 >= VM_STACK_MAX ){ return 1; }
	core->array[core->SP] = val;
	core->SP ++;

	return 0;
}

//--------------------------------------------------------------------------------------------
/**
 * 仮想マシンスタックポップ
 *
 * @param	core		仮想マシン制御構造体へのポインタ
 *
 * @return
 */
//--------------------------------------------------------------------------------------------
const VM_CODE * VMStackPop( VM_MACHINE * core )
{
	if( core->SP == 0 ){ return NULL; }
	core->SP --;

	return core->array[core->SP];
}

//--------------------------------------------------------------------------------------------
/**
 * 仮想マシンジャンプ命令
 *
 * @param	core		仮想マシン制御構造体へのポインタ
 * @param	adrs		ジャンプ先アドレス
 *
 * @return	none
 */
//--------------------------------------------------------------------------------------------
void VMJump( VM_MACHINE * core, VM_CODE * adrs )
{
	core->PC = adrs;
}

//--------------------------------------------------------------------------------------------
/**
 * 仮想マシンコール命令
 *
 * @param	core		仮想マシン制御構造体へのポインタ
 * @param	adrs		呼び出すアドレス
 *
 * @return	none
 */
//--------------------------------------------------------------------------------------------
void VMCall( VM_MACHINE * core, VM_CODE * adrs )
{
	VMStackPush( core, core->PC );
	core->PC = adrs;

#ifdef PM_DEBUG
	DebugEvent_LabelCallPush(core->fsys->event);
#endif
}

//--------------------------------------------------------------------------------------------
/**
 * 仮想マシンリターン命令
 *
 * @param	core		仮想マシン制御構造体へのポインタ
 *
 * @return	none
 */
//--------------------------------------------------------------------------------------------
void VMRet( VM_MACHINE * core )
{
	core->PC = VMStackPop( core );

#ifdef PM_DEBUG
	DebugEvent_LabelCallPop(core->fsys->event);
#endif
}


//--------------------------------------------------------------------------------------------
/**
 * PCのアドレスから16bit(2byte)データ取得
 *
 * @param	core		仮想マシン制御構造体へのポインタ
 *
 * @return	取得データ
 */
//--------------------------------------------------------------------------------------------
u16 VMGetU16( VM_MACHINE * core )
{
	u16	val;

	val = (u16)VMGetU8( core );
	val += (u16)VMGetU8( core ) << 8;

	return val;
}

//--------------------------------------------------------------------------------------------
/**
 * PCのアドレスから32bit(4byte)データ取得
 *
 * @param	core		仮想マシン制御構造体へのポインタ
 *
 * @return	取得データ
 */
//--------------------------------------------------------------------------------------------
u32 VMGetU32( VM_MACHINE * core )
{
	u32	val;
	u8	a,b,c,d;

	a = VMGetU8( core );
	b = VMGetU8( core );
	c = VMGetU8( core );
	d = VMGetU8( core );

	val = 0;
	val += (u32)d;
	val <<= 8;
	val += (u32)c;
	val <<= 8;
	val += (u32)b;
	val <<= 8;
	val += (u32)a;

	return val;
}
